package org.xxpay.mch.common.ctrl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.domain.BizResponse;
import org.xxpay.core.common.util.*;
import org.xxpay.core.common.vo.DBSmsConfig;
import org.xxpay.core.entity.MchInfo;
import org.xxpay.mch.common.config.MainConfig;
import org.xxpay.mch.common.service.CommonService;
import org.xxpay.mch.secruity.JwtUser;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author: dingzhiwei
 * @date: 17/12/6
 * @description:
 */
@Controller
public class BaseController {

    private static final MyLog baseLogger = MyLog.getLog(BaseController.class);


    @Autowired
    protected HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;

    @Autowired
    public CommonService commonService;

    @Autowired
    protected MainConfig mainConfig;

    private static final String REQ_PARAM_JSON_KEY = "REQ_CONTEXT_REQ_PARAM_JSON";  //将转换好的json对象保存在 reqContext对象中的key
    private static final int DEFAULT_PAGE_INDEX = 1;
    private static final int DEFAULT_PAGE_SIZE = 20;
    protected static final String PAGE_COMMON_ERROR = "common/error";
    protected static final String PAGE_COMMON_PC_ERROR = "common/pc_error";

    /**request.getParameter 获取参数 并转换为JSON格式 **/
    private JSONObject reqParam2JSON() {
        String params = request.getParameter("params");
        if(StringUtils.isNotBlank(params)) {
            return JSON.parseObject(params);
        }
        // 参数Map
        Map properties = request.getParameterMap();
        // 返回值Map
        JSONObject returnObject = new JSONObject();
        Iterator entries = properties.entrySet().iterator();
        Map.Entry entry;
        String name;
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            if(null == valueObj){
                value = "";
            }else if(valueObj instanceof String[]){
                String[] values = (String[])valueObj;
                for(int i=0;i<values.length;i++){
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length()-1);
            }else{
                value = valueObj.toString();
            }

            if(!name.contains("[")){
                returnObject.put(name, value);
                continue;
            }
            //添加对json对象解析的支持  example: {ps[abc] : 1}
            String mainKey = name.substring(0, name.indexOf("["));
            String subKey = name.substring(name.indexOf("[") + 1 , name.indexOf("]"));
            JSONObject subJson = new JSONObject();
            if(returnObject.get(mainKey) != null) {
                subJson = (JSONObject)returnObject.get(mainKey);
            }
            subJson.put(subKey, value);
            returnObject.put(mainKey, subJson);
        }
        return returnObject;
    }


    /** 获取json格式的请求参数 **/
    protected JSONObject getReqParamJSON(){

        //将转换好的reqParam JSON格式的对象保存在当前请求上下文对象中进行保存；
        // 注意1： springMVC的CTRL默认单例模式， 不可使用局部变量保存，会出现线程安全问题；
        // 注意2： springMVC的请求模式为线程池，如果采用ThreadLocal保存对象信息，可能会出现不清空或者被覆盖的问题。
        Object reqParamObject = RequestContextHolder.getRequestAttributes().getAttribute(REQ_PARAM_JSON_KEY, RequestAttributes.SCOPE_REQUEST);
        if(reqParamObject == null){
            JSONObject reqParam = reqParam2JSON();
            RequestContextHolder.getRequestAttributes().setAttribute(REQ_PARAM_JSON_KEY, reqParam, RequestAttributes.SCOPE_REQUEST);
            return reqParam;
        }
        return (JSONObject) reqParamObject;
    }

    /** 获取  经过AES加密的  json格式的请求参数 **/
    protected JSONObject getReqDecryptParamJSON(){
        try {
            JSONObject encryptParams = getReqParamJSON();
            if (encryptParams == null || StringUtils.isBlank(encryptParams.getString("enParams"))) {
                return null;
            }
            String decryprStr = MyAesReqEncrypt.desEncrypt(encryptParams.getString("enParams"));
            return JSONObject.parseObject(decryprStr);
        } catch (Exception e) {
            baseLogger.error("获取参数失败！ ", e);
            return null;
        }
    }


    /** 获取  AES加密参数 请求参数值 [ T 类型 ], [ 非必填 ] **/
    protected <T> T getDecryptVal(String key, Class<T> cls) {
        JSONObject decryprParams = getReqDecryptParamJSON();
        if (decryprParams == null) {
            return null;
        }
        return decryprParams.getObject(key, cls);
    }

    /** 获取  AES加密参数  请求参数值 [ T 类型 ], [ 必填 ] **/
    protected <T> T getDecryptValRequired(String key, Class<T> cls) {
        T value = getDecryptVal(key, cls);
        if(ObjectUtils.isEmpty(value)) {
            throw new RuntimeException(genParamRequiredMsg(key));
        }
        return value;
    }

    /** 生成参数必填错误信息 **/
    private String genParamRequiredMsg(String key) {
        return "参数" + key + "必填";
    }

    /** 获取  AES加密参数  请求参数值 [ T 类型 ], [ 如为null返回默认值 ] **/
    protected  <T> T getDecryptValDefault(String key, T defaultValue, Class<T> cls) {
        T value = getDecryptVal(key, cls);
        if(value == null) return defaultValue;
        return value;
    }

    /** 获取  经过AES加密的  请求参数值 String 类型相关函数 **/
    protected String getDecryptValString(String key) {
        return getDecryptVal(key, String.class);
    }
    protected String getDecryptValStringRequired(String key) {
        return getDecryptValRequired(key, String.class);
    }
    protected String getDecryptValStringDefault(String key, String defaultValue) {
        return getDecryptValDefault(key, defaultValue, String.class);
    }

    /** 获取  经过AES加密的  请求参数值 Byte 类型相关函数 **/
    protected Byte getDecryptValByte(String key) {
        return getDecryptVal(key, Byte.class);
    }
    protected Byte getDecryptValByteRequired(String key) {
        return getDecryptValRequired(key, Byte.class);
    }
    protected Byte getDecryptValByteDefault(String key, Byte defaultValue) {
        return getDecryptValDefault(key, defaultValue, Byte.class);
    }

    /** 获取  经过AES加密的  请求参数值 Long 类型相关函数 **/
    protected Long getDecryptValLong(String key) {
        return getDecryptVal(key, Long.class);
    }
    protected Long getDecryptValLongRequired(String key) {
        return getDecryptValRequired(key, Long.class);
    }
    protected Long getDecryptValLongDefault(String key, Long defaultValue) {
        return getDecryptValDefault(key, defaultValue, Long.class);
    }

    /** 获取  经过AES加密的  对象类型 **/
    protected <T> T getDecryptObject(Class<T> clazz) {
        JSONObject decryprParams = getReqDecryptParamJSON();
        if (decryprParams == null) {
            throw new RuntimeException("参数获取失败");
        }
        return decryprParams.toJavaObject(clazz);
    }

    protected JwtUser getUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        return jwtUser;
    }

    protected int getPageIndex(JSONObject object) {
        if(object == null) return DEFAULT_PAGE_INDEX;
        Integer pageIndex = object.getInteger("page");
        if(pageIndex == null) return DEFAULT_PAGE_INDEX;
        return pageIndex;
    }

    protected int getPageSize(JSONObject object) {
        if(object == null) return DEFAULT_PAGE_SIZE;
        Integer pageSize = object.getInteger("limit");
        if(pageSize == null) return DEFAULT_PAGE_SIZE;
        return pageSize;
    }

    protected int getPageIndex(Integer page) {
        if(page == null) return DEFAULT_PAGE_INDEX;
        return page;
    }

    protected int getPageSize(Integer limit) {
        if(limit == null) return DEFAULT_PAGE_SIZE;
        return limit;
    }

    protected JSONObject getJsonParam(HttpServletRequest request) {
        String params = request.getParameter("params");
        if(StringUtils.isNotBlank(params)) {
            return JSON.parseObject(params);
        }
        // 参数Map
        Map properties = request.getParameterMap();
        // 返回值Map
        JSONObject returnObject = new JSONObject();
        Iterator entries = properties.entrySet().iterator();
        Map.Entry entry;
        String name;
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            if(null == valueObj){
                value = "";
            }else if(valueObj instanceof String[]){
                String[] values = (String[])valueObj;
                for(int i=0;i<values.length;i++){
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length()-1);
            }else{
                value = valueObj.toString();
            }
            returnObject.put(name, value);
        }
        return returnObject;
    }

    /** 方法重载 **/
    protected JSONObject getJsonParam() {
        return getJsonParam(request);
    }

    protected Boolean getBoolean(JSONObject param, String key) {
        if(param == null) return null;
        return param.getBooleanValue(key);
    }

    protected String getString(JSONObject param, String key) {
        if(param == null) return null;
        return param.getString(key);
    }

    protected String getStringRequired(JSONObject param, String key) {
        if(param == null ) {
            throw new RuntimeException(getErrMsg(key));
        }
        String value = param.getString(key);
        if(StringUtils.isBlank(value)) {
            throw new RuntimeException(getErrMsg(key));
        }
        return value;
    }

    protected String getStringDefault(JSONObject param, String key, String defaultValue) {
        String value = getString(param, key);
        if(value == null) return defaultValue;
        return value;
    }

    protected Byte getByte(JSONObject param, String key) {
        if(param == null) return null;
        return param.getByte(key);
    }

    protected Byte getByteRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Byte value = param.getByte(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected int getByteDefault(JSONObject param, String key, byte defaultValue) {
        Byte value = getByte(param, key);
        if(value == null) return defaultValue;
        return value.byteValue();
    }

    protected Integer getInteger(JSONObject param, String key) {
        if(param == null) return null;
        return param.getInteger(key);
    }

    protected Integer getIntegerRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Integer value = param.getInteger(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected int getIntegerDefault(JSONObject param, String key, int defaultValue) {
        Integer value = getInteger(param, key);
        if(value == null) return defaultValue;
        return value.intValue();
    }

    protected Long getLong(JSONObject param, String key) {
        if(param == null) return null;
        return param.getLong(key);
    }

    protected Long getLongRequired(JSONObject param, String key) {
        if(param == null ) throw new RuntimeException(getErrMsg(key));
        Long value = param.getLong(key);
        if(value == null) throw new RuntimeException(getErrMsg(key));
        return value;
    }

    protected long getLongDefault(JSONObject param, String key, long defaultValue) {
        Long value = getLong(param, key);
        if(value == null) return defaultValue;
        return value.longValue();
    }

    protected JSONObject getJSONObject(JSONObject param, String key) {
        if(param == null) return null;
        return param.getJSONObject(key);
    }

    protected <T> T getObject(JSONObject param, String key, Class<T> clazz) {
        JSONObject object = getJSONObject(param, key);
        if(object == null) return null;
        return JSON.toJavaObject(object, clazz);
    }

    protected <T> T getObject(JSONObject param, Class<T> clazz) {
        if(param == null) return null;
        return JSON.toJavaObject(param, clazz);
    }

    private String getErrMsg(String key) {
        return "参数" + key + "必填";
    }

    /**
     * 验证谷歌验证码
     * @param googleAuthSecretKey
     * @param code
     * @return
     */
    protected boolean checkGoogleCode(String googleAuthSecretKey, Long code) {
        if(StringUtils.isBlank(googleAuthSecretKey)) {
            return false;
        }
        // 谷歌验证
        long t = System.currentTimeMillis();
        GoogleAuthenticator ga = new GoogleAuthenticator();
        ga.setWindowSize(5);    // 最多可偏移的时间
        return ga.check_code(googleAuthSecretKey, code, t);
    }

    /**
     * 验证支付(如果验证不通过返回BizResponse对象,否则返回null)
     * @param mchInfo
     * @param param
     * @return
     */
    protected BizResponse verifyPay(MchInfo mchInfo, JSONObject param) {
        // 支付安全验证类型
        Byte paySecurityType = mchInfo.getPaySecurityType();
        switch (paySecurityType) {
            case 0: // 不需验证
                break;
            case 1: // 仅验证支付密码
                String payPassword = getStringRequired(param, "payPassword");   // 支付密码
                String passwordMd5 = MD5Util.string2MD5(payPassword);
                // 判断输入支付密码是否正确
                if(!mchInfo.getPayPassword().equals(passwordMd5)) {
                    return BizResponse.build(RetEnum.RET_MCH_PAP_PASSWORD_NOT_MATCH);
                }
                break;
            case 2: // 仅验证谷歌密码
                Long googleCode = getLongRequired(param, "googleCode");
                if(!checkGoogleCode(mchInfo.getGoogleAuthSecretKey(), googleCode)) {
                    return BizResponse.build(RetEnum.RET_MCH_GOOGLECODE_NOT_MATCH);
                }
                break;
            case 3: // 支付密码和谷歌验证码组合
                payPassword = getStringRequired(param, "payPassword");   // 支付密码
                passwordMd5 = MD5Util.string2MD5(payPassword);
                // 判断输入支付密码是否正确
                if(!mchInfo.getPayPassword().equals(passwordMd5)) {
                    return BizResponse.build(RetEnum.RET_MCH_PAP_PASSWORD_NOT_MATCH);
                }
                googleCode = getLongRequired(param, "googleCode");
                if(!checkGoogleCode(mchInfo.getGoogleAuthSecretKey(), googleCode)) {
                    return BizResponse.build(RetEnum.RET_MCH_GOOGLECODE_NOT_MATCH);
                }
                break;
            default:
                return null;
        }
        return null;
    }

    /**
     * 参数值不能为空
     * @param param
     * @param keys
     */
    protected void checkRequired(JSONObject param, String... keys) {
        if(param == null ) throw new RuntimeException(getErrMsg(keys[0]));
        for(String key : keys) {
            String value = param.getString(key);
            if(value == null) throw new RuntimeException(getErrMsg(key));
        }
    }

    /**
     * 参数值不能为空,不能以"-"开头
     * @param param
     * @param keys
     */
    public void checkRequired2(JSONObject param, String... keys) {
        if(param == null ) throw new RuntimeException(getErrMsg(keys[0]));
        for(String key : keys) {
            String value = param.getString(key);
            if(value == null) throw new RuntimeException(getErrMsg(key));
            if(value.startsWith("-")) throw new RuntimeException("参数" + key + "不能小于0");
        }
    }

    /**
     * 得到前端传入的金额元,转换成长整型分
     * @param param
     * @param name
     * @return
     */
    public Long getRequiredAmountL(JSONObject param, String name) {
        String amountStr = getStringRequired(param, name);  // 前端填写的为元,可以为小数点2位
        Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).longValue(); // // 转成分
        return amountL;
    }

    /**
     * 处理参数中的金额(将前端传入金额元转成分)
     * @param param
     * @param names
     */
    public void handleParamAmount(JSONObject param, String... names) {
        for(String name : names) {
            String amountStr = getString(param, name);  // 前端填写的为元,可以为小数点2位
            if(StringUtils.isNotBlank(amountStr)) {
                Long amountL = new BigDecimal(amountStr.trim()).multiply(new BigDecimal(100)).longValue(); // // 转成分
                param.put(name, amountL);
            }
        }
    }

    public Map<String, Object> request2payResponseMap(HttpServletRequest request, String[] paramArray) {
        Map<String, Object> responseMap = new HashMap<>();
        for (int i = 0;i < paramArray.length; i++) {
            String key = paramArray[i];
            String v = request.getParameter(key);
            if (v != null) {
                responseMap.put(key, v);
            }
        }
        return responseMap;
    }

    /**
     * 生成公共查询参训
     * @param param
     * @return
     */
    public JSONObject getQueryObj(JSONObject param) {
        // 订单起止时间
        Date createTimeStart = null;
        Date createTimeEnd = null;
        String createTimeStartStr = getString(param, "createTimeStart");
        if(StringUtils.isNotBlank(createTimeStartStr)) createTimeStart = DateUtil.str2date(createTimeStartStr);
        String createTimeEndStr = getString(param, "createTimeEnd");
        if(StringUtils.isNotBlank(createTimeEndStr)) createTimeEnd = DateUtil.str2date(createTimeEndStr);
        JSONObject queryObj = new JSONObject();
        queryObj.put("createTimeStart", createTimeStart);
        queryObj.put("createTimeEnd", createTimeEnd);
        return queryObj;
    }

    public void sendSms(String telNo, Byte bizType, String templateVal) throws ClientException {

        DBSmsConfig smsConfig = commonService.sysConfigService.getSmsConfig();
        DBSmsConfig.SmsTemplate templateConfig = smsConfig.getByBizId(bizType);

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile(DBSmsConfig.regionId, smsConfig.getAccessKeyId(), smsConfig.getAccessKeySecret());
        DefaultProfile.addEndpoint(DBSmsConfig.endpointName, DBSmsConfig.regionId, DBSmsConfig.product, DBSmsConfig.domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SendSmsRequest request = new SendSmsRequest();
        //必填:待发送手机号
        request.setPhoneNumbers(telNo);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName(smsConfig.getSignName());
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templateConfig.getTemplateCode());
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        request.setTemplateParam(templateVal);

        //hint 此处可能会抛出异常，注意catch
        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);

        baseLogger.info("调用阿里短信服务完成.mobile={},code={},message={},requestId={},bizId={}", telNo,
                sendSmsResponse.getCode(), sendSmsResponse.getMessage(), sendSmsResponse.getRequestId(), sendSmsResponse.getBizId());

    }

    /**
     * 输出excel数据
     * @param fileName
     * @param data
     */
    protected void writeExcelStream(String fileName, List<List> data) {

        try {
            response.setHeader("Content-disposition", "attachment;filename="
                    + new String((fileName + ".xlsx").getBytes("gb2312"), "ISO8859-1"));//设置文件头编码格式
            response.setContentType("APPLICATION/OCTET-STREAM;charset=UTF-8");//设置类型

            Workbook workbook = new XSSFWorkbook(); //生成excel 2007格式
            Sheet sheet = workbook.createSheet();

            for(int i = 0; i < data.size(); i++){
                Row row = sheet.createRow(i);

                for(int j = 0; j< data.get(i).size(); j++){
                    Cell cell = row.createCell(j);
                    if(data.get(i).get(j) != null) {
                        cell.setCellValue(data.get(i).get(j).toString());
                    }
                }
            }
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            baseLogger.error("writeExcelStream", e);
        }
    }


}
